package com.netx.ebs;
import com.netx.data.Connection;
import com.netx.data.DatabaseException;
import com.netx.data.NoSuchInstanceException;
import com.netx.data.ValueList;
import com.netx.generics.basic.Checker;
import com.netx.generics.basic.IntegrityException;
import com.netx.generics.sql.Row;
import com.netx.generics.sql.Table;
import com.netx.generics.time.Date;
import com.netx.generics.time.Moment;
import com.netx.generics.util.Arrays;


public class Users extends EbsEntity {

	public Users() {
		super();
	}

	public long add(String username, String password, String ipAddress, String name, char sex, Date birthdate, int sessionTimeoutTime, Role role, Permission[] permissions) throws DatabaseException {
		Checker.checkNull(role, "role");
		Checker.checkEmpty(permissions, "permissions");
		ValueList values = new ValueList();
		values.addValue("username", username);
		values.addValue("password", password);
		values.addValue("ip_address", ipAddress);
		values.addValue("name", name);
		values.addValue("sex", sex+"");
		values.addValue("birthdate", birthdate);
		values.addValue("registration_date", new Moment().getDate());
		values.addValue("session_timeout_time", sessionTimeoutTime);
		values.addValue("role_id", role.getPrimaryKey()+"");

		Connection c = getConnection();
		synchronized(c) {
			startTransaction();
			long userID = insert(values);
			for(int i=0; i<permissions.length; i++) {
				insertRelation(userID, permissions[i].getPrimaryKey());
			}
			commit();
			return userID;
		}
	}

	public void change(long userID, ValueList values) throws DatabaseException {
		Checker.checkNull(values, "values");
		if(update(userID, values) == 0) {
			throw new NoSuchInstanceException("userID: "+userID);
		}
		// change existing session:
		Session s = sManager.getSession(userID);
		if(s != null) {
			s.setUser(_retrieveUser(userID));
		}
	}

	public void change(long userID, Role role) throws DatabaseException {
		Checker.checkNull(role, "role");
		ValueList values = new ValueList();
		values.addValue("role_id", role.getPrimaryKey());
		if(update(userID, values) == 0) {
			throw new NoSuchInstanceException("userID: "+userID);
		}
		// change existing session:
		Session s = sManager.getSession(userID);
		if(s != null) {
			s.getUser().setRole(role);
		}
	}

	public void change(long userID, Permission[] permissions) throws DatabaseException {
		Checker.checkNullElements(permissions, "permissions");
		if(!exists(userID)) {
			throw new NoSuchInstanceException("userID: "+userID);
		}
		else {
			synchronized(getConnection()) {
				startTransaction();
				for(int i=0; i<permissions.length; i++) {
					insertRelation(userID, permissions[i].getPrimaryKey());
				}
				commit();
			}
			// change existing session:
			Session s = sManager.getSession(userID);
			if(s != null) {
				s.getUser().setPermissions(permissions);
			}
		}
	}
	
	public boolean remove(long userID, boolean force) throws DatabaseException {
		Session s = sManager.getSession(userID);
		if(s == null) {
			if(delete(userID) == 0) {
				throw new NoSuchInstanceException("userID: "+userID);
			}
			else {
				return true;
			}
		}
		else if(force) {
			sManager.terminateSession(s, "user was forcibly removed from the database");
			if(delete(userID) == 0) {
				throw new IntegrityException();
			}
			return true;
		}
		else {
			return false;
		}
	}
	
	public User find(long userID) throws DatabaseException {
		Session s = sManager.getSession(userID);
		if(s != null) {
			return s.getUser();
		}
		else {
			return _retrieveUser(userID);
		}
	}

	public User find(String username) throws DatabaseException {
		Checker.checkEmpty(username, "username");
		Table result = getConnection().select("user_id FROM users WHERE username=\""+username+"\"");
		if(result.isEmpty()) {
			return null;
		}
		else {
			return find(result.getRow(0).getLong(1));
		}
	}

	// For AuthenticatorServlet:
	User find(String username, String password) throws DatabaseException {
		Checker.checkEmpty(username, "username");
		Checker.checkEmpty(password, "password");
		Table result = getConnection().select("user_id FROM users WHERE username=\""+username+"\" AND password=\""+password+"\"");
		if(result.isEmpty()) {
			return null;
		}
		else {
			return find(result.getRow(0).getLong(1));
		}
	}
	
	// For AuthenticatorServlet:
	User findByAddress(String IPAddress) throws DatabaseException {
		Checker.checkEmpty(IPAddress, "IPAddress");
		Table result = getConnection().select("user_id FROM users WHERE ip_address=\""+IPAddress+"\"");
		if(result.isEmpty()) {
			return null;
		}
		else {
			return find(result.getRow(0).getLong(1));
		}
	}

	private Permission[] _findPermissions(long userID) throws DatabaseException {
		Table t = getConnection().select("permissions.permission_id, permissions.name FROM permissions, user_permissions, users WHERE permissions.permission_id=user_permissions.permission_id AND user_permissions.user_id=users.user_id AND users.user_id="+userID);
		Permission[] permissions = new Permission[t.getRowCount()];
		for(int i=0; i<t.getRowCount(); i++) {
			permissions[i] = new Permission(t.getRow(i));
		}
		return permissions;
	}

	private User _retrieveUser(long userID) throws DatabaseException {
		Row data = select("user_id, username, password, name, sex, birthdate, registration_date, session_timeout_time, role_id", userID);
		if(data == null) {
			return null;
		}
		else {
			Roles roles = (Roles)getConnection().getEntity("roles");
			Role role = roles.find(data.getLong(9));
			Permission[] perms = Arrays.concat(roles.findPermissions(data.getLong(9)), _findPermissions(data.getLong(1)));
			return new User(data, role, perms);
		}
	}
}
